﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace LightCAD.MathLib
{

    public static class StringExt
    {
        public static string toLowerCase(this string str)
        {
            return str.ToLower();
        }
    }
    public static class ArrayExt
    {
        public static void set(this Array arr, Array value, int offset = 0)
        {
            var len = value.Length;
            if (len + offset > arr.Length)
            {
                len = arr.Length - offset;
            }
            Array.Copy(value, 0, arr, offset, len);
        }
        public static Array subarray(this Array arr, int begin = -1, int end = -1)
        {
            if (arr.Length == 0 || begin == -1)
                return (Array)arr.Clone();

            if (end == -1)
                end = arr.Length;

            var subarr = Array.CreateInstance(arr.GetValue(0).GetType(), end - begin);
            Array.Copy(arr, begin, subarr, 0, end - begin);
            return subarr;
        }
        public static void mapStr(this Array arr, Func<string, string> func)
        {
            var sarr = (string[])arr;
            for (var i = 0; i < arr.Length; i++)
            {
                sarr[i] = func(sarr[i]);
            }
        }
        public static void mapFloat(this Array arr, Func<double, double> func)
        {
            var farr = (double[])arr;
            for (var i = 0; i < arr.Length; i++)
            {
                farr[i] = func(farr[i]);
            }
        }
        public static K[] map<T, K>(this Array arr, Func<T, K> func)
        {
            var marr = new K[arr.Length];
            var oarr = (T[])arr;
            for (var i = 0; i < arr.Length; i++)
            {
                marr[i] = func(oarr[i]);
            }
            return marr;
        }
        public static void forEachStr(this Array arr, Action<string, int> func)
        {
            var sarr = (string[])arr;
            for (var i = 0; i < arr.Length; i++)
            {
                func(sarr[i], i);
            }
        }
        public static void forEach(this Array arr, Action<object, int> func)
        {
            for (var i = 0; i < arr.Length; i++)
            {
                func(arr.GetValue(i), i);
            }
        }
        public static T[] slice<T>(this Array arr, int start, int end = -1)
        {
            if (end == -1) end = arr.Length;
            var len = end - start;
            var narr = new T[len];
            Array.Copy(arr, start, narr, 0, len);
            return narr;
        }
        public static T[] slice2<T>(this Array arr, int start, int len)
        {
            var narr = new T[len];
            Array.Copy(arr, start, narr, 0, len);
            return narr;
        }
        public static T[] slice<T>(this Array arr)
        {
            var narr = new T[arr.Length];
            Array.Copy(arr, narr, arr.Length);
            return narr;
        }
        public static int bytesPerItem(this Array arr)
        {

            if (arr is float[])
            {
                return 4;
            }
            else if (arr is double[])
            {
                return 8;
            }
            else if (arr is ushort[])
            {
                return 2;
            }
            else if (arr is short[])
            {
                return 2;
            }
            else if (arr is int[])
            {
                return 4;
            }
            else if (arr is uint[])
            {
                return 4;
            }
            else if (arr is sbyte[])
            {
                return 1;
            }
            else if (arr is byte[])
            {
                return 1;
            }
            else
                return 0;

        }
        public static int byteSize(this Array arr)
        {
            if (arr.Length == 0) return 0;
            return arr.bytesPerItem() * arr.Length;
        }


        public static int bytesPerItem(this IList arr)
        {
            if (arr is IList<float>)
            {
                return 4;
            }
            else if (arr is IList<double>)
            {
                return 8;
            }
            else if (arr is IList<ushort>)
            {
                return 2;
            }
            else if (arr is IList<short>)
            {
                return 2;
            }
            else if (arr is IList<int>)
            {
                return 4;
            }
            else if (arr is IList<uint>)
            {
                return 4;
            }
            else if (arr is IList<sbyte>)
            {
                return 1;
            }
            else if (arr is IList<byte>)
            {
                return 1;
            }
            else
                return 0;
        }
        public static int byteSize(this IList arr) 
        {
            return arr.Count * arr.bytesPerItem();
        }

        public static bool floatToFloatEqual(this Array arrA, Array arrB)
        {
            if (arrA.Length != arrB.Length)
                return false;
            var farrA = (double[])arrA;
            var farrB = (double[])arrB;
            for (var i = 0; i < farrA.Length; i++)
            {
                if (farrA[i] != farrB[i]) return false;
            }
            return true;
        }
        public static bool floatToIntEqual(this Array arrA, Array arrB)
        {
            if (arrA.Length != arrB.Length)
                return false;
            var farrA = (double[])arrA;
            var iarrB = (int[])arrB;
            for (var i = 0; i < farrA.Length; i++)
            {
                if (farrA[i] != iarrB[i]) return false;
            }
            return true;
        }
        public static void floatCopyFormInt(this Array arrA, Array arrB)
        {
            if (arrA.Length != arrB.Length)
                return;
            var farrA = (double[])arrA;
            var iarrB = (int[])arrB;
            for (var i = 0; i < farrA.Length; i++)
            {
                farrA[i] = iarrB[i];
            }
        }
        public static void CopySubFromArray(this Array dest, int deststart, Array source,int srcIndex,int count)
        {
          
            Array.Copy(source, srcIndex, dest, deststart, count);
        }
        public static void floatCopyFormFloat(this Array arrA, Array arrB)
        {
            if (arrA.Length != arrB.Length)
                return;
            var farrA = (double[])arrA;
            var farrB = (double[])arrB;
            for (var i = 0; i < farrA.Length; i++)
            {
                farrA[i] = farrB[i];
            }
        }
        public static double[] intToDouble(this Array arr)
        {
            var farr = (int[])arr;
            var darr = new double[farr.Length];
            for (var i = 0; i < farr.Length; i++)
            {
                darr[i] = farr[i];
            }
            return darr;
        }
        public static double[] uintToDouble(this Array arr)
        {
            var farr = (uint[])arr;
            var darr = new double[farr.Length];
            for (var i = 0; i < farr.Length; i++)
            {
                darr[i] = farr[i];
            }
            return darr;
        }
        public static int[] ushortToint(this Array arr)
        {
            var farr = (ushort[])arr;
            var darr = new int[farr.Length];
            for (var i = 0; i < farr.Length; i++)
            {
                darr[i] = farr[i];
            }
            return darr;
        }
        public static double[] toDouble(this Array sarr)
        {
            if (sarr.Length == 0) return new double[0];
            var type = sarr.GetValue(0).GetType().Name;
            var darr = new double[sarr.Length];

            if (type == "Double")
            {
                return (double[])sarr;
            }
            else if (type == "SByte")
            {
                var arr = (sbyte[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "Byte")
            {
                var arr = (byte[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "Int16")
            {
                var arr = (short[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "UInt16")
            {
                var arr = (ushort[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "Single")
            {
                var arr = (float[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
                }
            else if (type == "Int32")
            {
                var arr = (int[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "UInt32")
            {
                var arr = (uint[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "Int64")
            {
                var arr = (long[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else if (type == "UInt64")
            {
                var arr = (ulong[])sarr;
                for (var i = 0; i < arr.Length; i++) darr[i] = arr[i];
                return darr;
            }
            else
                return null;
        }
        public static double[] floatToDouble(this Array arr)
        {
            var farr = (float[])arr;
            var darr = new double[farr.Length];
            for (var i = 0; i < farr.Length; i++)
            {
                darr[i] = farr[i];
            }
            return darr;
        }
        public static float[] doubleToFloat(this Array arr)
        {
            var darr = (double[])arr;
            var farr = new float[darr.Length];
            for (var i = 0; i < darr.Length; i++)
            {
                farr[i] = (float)darr[i];
            }
            return farr;
        }
    }

    public static class ObjectExt
    {
        public static bool HasMethod(this Object obj, string methodName)
        {
            var method = obj.GetMethod(methodName);
            return (method != null);
        }
        public static object GetMethod(this Object obj, string methodName)
        {
            var method = obj.GetType().GetMethod(methodName);
            return method;
        }
        public static object InvokeMethod(this Object obj, string methodName, params object[] args)
        {
            var method = obj.GetType().GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            return method.Invoke(obj, args);
        }
        public static bool HasProperty(this Object obj, string propName)
        {
            var prop = obj.GetType().GetProperty(propName);
            return (prop != null);
        }
        public static object GetProperty(this Object obj, string propName)
        {
            var prop = obj.GetType().GetProperty(propName);
            if (prop == null) return null;
            return prop?.GetValue(obj);
        }
        public static void SetProperty(this Object obj, string propName, object propValue)
        {
            var prop = obj.GetType().GetProperty(propName);
            prop.SetValue(obj, propValue);
        }
        public static string[] GetFields(this Object obj, Dictionary<string, FieldInfo> fieldDict = null)
        {
            var fields = obj.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            if (fieldDict != null)
            {
                fieldDict.Clear();
                for (int i = 0; i < fields.Length; i++)
                {
                    var field = fields[i];
                    fieldDict.Add(field.Name, field);
                }
            }
            return fields.Select(f => f.Name).ToArray();
        }
        public static bool HasField(this Object obj, string fieldName)
        {
            var fld = obj.GetType().GetField(fieldName);
            return fld != null;
        }
        public static object GetField(this Object obj, string fieldName)
        {
            FieldInfo fld = null;
            if (obj is Type)
                fld = (obj as Type).GetField(fieldName);
            else
                fld = obj.GetType().GetField(fieldName);
            if (fld == null) return null;
            return fld.GetValue(obj);
        }
        public static object GetField(this Object obj, string fieldName, object defaultValue)
        {
            var fld = obj.GetType().GetField(fieldName);
            if (fld == null) return defaultValue;
            return fld.GetValue(obj);
        }
        public static void SetField(this Object obj, string fieldName, object fieldValue)
        {
            var fld = obj.GetType().GetField(fieldName);
            if (fld == null) return;
            fld.SetValue(obj, fieldValue);
        }
        public static bool HasFieldOrProperty(this Object obj, string name)
        {
            return HasField(obj, name) || HasProperty(obj, name);
        }
        public static object GetFieldOrProperty(this Object obj, string name, object defaultValue = null)
        {
            var type = obj.GetType();
            var fld = type.GetField(name);
            if (fld == null)
            {
                var prop = type.GetProperty(name);
                if (prop == null)
                    return defaultValue;
                else
                    return prop.GetValue(obj);
            }
            else
                return fld.GetValue(obj);
        }
        public static object GetFieldOrPropertyOrMethod(this Object obj, string name, object defaultValue = null)
        {
            var result = obj.GetFieldOrProperty(name, defaultValue);
            if (result == null)
                result = obj.GetMethod(name);
            return result ?? defaultValue;
        }
        public static bool SetFieldOrProperty(this Object obj, string name, object value)
        {
            var type = obj.GetType();
            var fld = type.GetField(name);
            if (fld == null)
            {
                var prop = type.GetProperty(name);
                if (prop == null)
                    return false;
                else
                    prop.SetValue(obj, value);
            }
            else
                fld.SetValue(obj, value);

            return true;
        }
        //public static T p<T>(this Object obj, string propName, object defaultValue = null)
        //{
        //    var prop = obj.GetType().GetProperty(propName);
        //    if (prop == null) return (T)defaultValue;
        //    return (T)prop.GetValue(obj);
        //}
        //public static void pset(this Object obj, string propName, object propValue)
        //{
        //    var prop = obj.GetType().GetProperty(propName);
        //    if (prop == null) return;
        //    prop.SetValue(obj, propValue);
        //}

        //public static T f<T>(this Object obj, string fieldName, object defaultValue = null)
        //{
        //    var field = obj.GetType().GetField(fieldName);
        //    if (field == null) return (T)defaultValue;
        //    return (T)field.GetValue(obj);
        //}
        //public static void fset(this Object obj, string fieldName, object fieldValue)
        //{
        //    var field = obj.GetType().GetField(fieldName);
        //    if (field == null) return;
        //    field.SetValue(obj, fieldValue);
        //}


    }
}
